package cn.kgc.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.kgc.base.dto.AdminUserDTO;
import cn.kgc.base.entity.JwtToken;
import cn.kgc.base.entity.LoginUserInfo;
import cn.kgc.commons.constant.SystemConstant;
import cn.kgc.commons.util.RedisUtil;
import cn.kgc.commons.util.UserContextHolder;
import cn.kgc.constant.GateWayConstant;
import cn.kgc.util.JwtTokenParseUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @description:
 * @author: 石浩杰
 * @date: 2022-12-02 12:04
 */
@Component
public class GlobalLoginRegisterFilter implements GlobalFilter, Ordered {

    @Resource
    private DiscoveryClient discoveryClient;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private RedisUtil redisUtil;

    /**
     * 从请求体中获得参数
     *
     * @param request
     * @return
     */
    private String parseBodyFormRequest(ServerHttpRequest request) {

        AtomicReference<String> atomicReference=new AtomicReference<>();
        //获得请求体
        Flux<DataBuffer> flux= request.getBody();
        //从请求获得参数
        flux.subscribe(dataBuffer -> {
            DataBufferUtils.release(dataBuffer);
            //将dataBuffer中的数据转成charBuffer
            CharBuffer charBuffer= StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
            atomicReference.set(charBuffer.toString());
        });
        return atomicReference.toString();
    }


    /**
     * 从认证中心中获得令牌
     *
     * @param request 请求的信息
     * @param urlFormat 认证中心的地址（从哪个地址上获得令牌 登录 注册）
     * @return
     */
    private String getTokenFromAuthorCenter(ServerHttpRequest request,
                                            String urlFormat) {
        String value=  parseBodyFormRequest(request);
        AdminUserDTO userNameAndPasswordDTO=
                JSON.parseObject(value,AdminUserDTO.class);
        List<ServiceInstance> serviceInstances=
                discoveryClient.getInstances("mall-authority-center");
        ServiceInstance serviceInstance=null;
        if(CollectionUtil.isEmpty(serviceInstances)){
            return null;
        }
        serviceInstance= serviceInstances.get(0);
        String host=serviceInstance.getHost();
        int port=serviceInstance.getPort();
        String url=String.format(urlFormat,host,port);
        HttpHeaders headers=new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity httpEntity=new HttpEntity(userNameAndPasswordDTO,headers);
        JwtToken jwtToken=restTemplate.postForObject(url,httpEntity,JwtToken.class);
        if(jwtToken!=null){
            return jwtToken.getToken();
        }
        return null;
    }
    //让用户强制下线：将登录的令牌保存到Redis中，指定令牌的存活时间，超过时间，令牌直接从redis删除每次验证令牌的时候
    //1 判断Redis中是否有该对象，如果用户没有令牌，直接认为登录失败
    //2 如果有令牌，解析令牌的数据,获得令牌对应的用户信息
    //3 使用令牌从Redis中获得用户的信息
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request=exchange.getRequest();
        ServerHttpResponse response=exchange.getResponse();
        String uri=request.getURI().toString();
        if(uri.contains(GateWayConstant.LOGIN_URL)){//登录操作
            String token=
                    getTokenFromAuthorCenter(request,GateWayConstant.URL_FORMAT_LOGIN);
            if(token==null){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
            }else{
                redisUtil.set(token,token,SystemConstant.PHONE_CODE_TIMEOUT, TimeUnit.DAYS);//令牌在redis中保存一天
                response.setStatusCode(HttpStatus.OK);
            }
            response.getHeaders().add(SystemConstant.JWT_USER_INFO_KEY,token);
            return response.setComplete();
        }
        //不是登录注册 获得提交的参数中 的令牌，解析令牌，获得用户信息。将用户信息保存到请求中
        //过滤器放行时
        if(uri.equals(GateWayConstant.REGISTER_URL)){
            String token=getTokenFromAuthorCenter(request,GateWayConstant.URL_FORMAT_REGISTER);
            if(token==null){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
            }else{
                redisUtil.set(token,token,SystemConstant.PHONE_CODE_TIMEOUT, TimeUnit.DAYS);//令牌在redis中保存一天
                response.setStatusCode(HttpStatus.OK);
            }
            response.getHeaders().add(SystemConstant.JWT_USER_INFO_KEY,token);
            response.setStatusCode(HttpStatus.OK);
            return response.setComplete();
        }
        //如果地址是/auth/code地址放行
        if (request.getURI().toString().contains(GateWayConstant.URL_FORMAT_AUTH_CODE)){
            return chain.filter(exchange);
        }
        //从请求头中获得令牌
        String token= request.getHeaders().getFirst(SystemConstant.JWT_USER_INFO_KEY);
        if (token==null){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        LoginUserInfo loginUserInfo=null;
        try {
            loginUserInfo= JwtTokenParseUtil.parseToken(token);
        } catch (Exception e) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        String username=loginUserInfo.getUsername() ;
        String userId=loginUserInfo.getId()+"";
        ServerHttpRequest.Builder builder= request.mutate();
        builder.header(UserContextHolder.LOGIN_USER_NAME,username);
        builder.header(UserContextHolder.LOGIN_USER_ID,userId);
        exchange=exchange.mutate().request(builder.build()).build();
        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE+1;
    }
}
